diff --git a/hamlpy/hamlpy.py b/hamlpy/hamlpy.py
index 640b6d6..dd92f25 100755
--- a/hamlpy/hamlpy.py
+++ b/hamlpy/hamlpy.py
@@ -8,26 +8,32 @@
VALID_EXTENSIONS = ['haml', 'hamlpy']
-class Compiler:
+DEFAULT_OPTIONS = {
+ 'attr_wrapper': '\'', # how to render attribute values, e.g. foo='bar'
+ 'django_inline_style': True, # support both #{...} and ={...}
+ 'debug_tree': False
+}
+
+class Compiler:
def __init__(self, options_dict=None):
- options_dict = options_dict or {}
- self.debug_tree = options_dict.pop('debug_tree', False)
- self.options_dict = options_dict
+ self.options = DEFAULT_OPTIONS.copy()
+ if options_dict:
+ self.options.update(options_dict)
def process(self, raw_text):
split_text = raw_text.split('\n')
return self.process_lines(split_text)
def process_lines(self, haml_lines):
- root = RootNode(**self.options_dict)
+ root = RootNode(self.options)
line_iter = iter(haml_lines)
haml_node=None
for line_number, line in enumerate(line_iter):
node_lines = line
- if not root.parent_of(HamlNode(line)).inside_filter_node():
+ if not root.parent_of(HamlNode(line, self.options)).inside_filter_node():
if line.count('{') - line.count('}') == 1:
start_multiline = line_number # For exception handling
@@ -42,11 +48,11 @@ def process_lines(self, haml_lines):
if haml_node is not None and len(node_lines.strip()) == 0:
haml_node.newlines += 1
else:
- haml_node = create_node(node_lines)
+ haml_node = create_node(node_lines, self.options)
if haml_node:
root.add_node(haml_node)
- if self.options_dict and self.options_dict.get('debug_tree'):
+ if self.options['debug_tree']:
return root.debug_tree()
else:
return root.render()
diff --git a/hamlpy/hamlpy_watcher.py b/hamlpy/hamlpy_watcher.py
index 98b9f63..89d0f34 100644
--- a/hamlpy/hamlpy_watcher.py
+++ b/hamlpy/hamlpy_watcher.py
@@ -51,6 +51,8 @@ def __call__(self, parser, namespace, values, option_string=None):
help='Add self closing tag. eg. --tag macro:endmacro')
arg_parser.add_argument('--attr-wrapper', dest='attr_wrapper', type=str, choices=('"', "'"), default="'", action='store',
help="The character that should wrap element attributes. This defaults to ' (an apostrophe).")
+arg_parser.add_argument('--django-inline', dest='django_inline', action='store_true',
+ help="Whether to support ={...} syntax for inline variables in addition to #{...}")
arg_parser.add_argument('--jinja', default=False, action='store_true',
help='Makes the necessary changes to be used with Jinja2.')
arg_parser.add_argument('--once', default=False, action='store_true',
@@ -91,6 +93,9 @@ def watch_folder():
if args.attr_wrapper:
compiler_args['attr_wrapper'] = args.attr_wrapper
+
+ if args.django_inline:
+ compiler_args['django_inline_style'] = args.django_inline
if args.jinja:
for k in ('ifchanged', 'ifequal', 'ifnotequal', 'autoescape', 'blocktrans',
diff --git a/hamlpy/nodes.py b/hamlpy/nodes.py
index a6d3e91..116fe34 100644
--- a/hamlpy/nodes.py
+++ b/hamlpy/nodes.py
@@ -40,9 +40,6 @@ class NotAvailableError(Exception):
VARIABLE = '='
TAG = '-'
-INLINE_VARIABLE = re.compile(r'(? 1 else 'utf-8'
self.before = "" % (
- self.attr_wrapper, self.attr_wrapper,
- self.attr_wrapper, encoding, self.attr_wrapper,
+ attr_wrapper, attr_wrapper,
+ attr_wrapper, encoding, attr_wrapper,
)
else:
types = {
@@ -418,8 +440,9 @@ def _post_render(self):
pass
class VariableNode(ElementNode):
- def __init__(self, haml):
- ElementNode.__init__(self, haml)
+ def __init__(self, haml, options):
+ super(VariableNode, self).__init__(haml, options)
+
self.django_variable = True
def _render(self):
@@ -455,8 +478,9 @@ class TagNode(HamlNode):
'for':'empty',
'with':'with'}
- def __init__(self, haml):
- HamlNode.__init__(self, haml)
+ def __init__(self, haml, options):
+ super(TagNode, self).__init__(haml, options)
+
self.tag_statement = self.haml.lstrip(TAG).strip()
self.tag_name = self.tag_statement.split(' ')[0]
@@ -478,7 +502,6 @@ def _render(self):
def should_contain(self, node):
return isinstance(node, TagNode) and node.tag_name in self.may_contain.get(self.tag_name, '')
-
class FilterNode(HamlNode):
def add_node(self, node):
self.add_child(node)
@@ -502,10 +525,10 @@ def _post_render(self):
# Don't post-render children of filter nodes as we don't want them to be interpreted as HAML
pass
-
class PlainFilterNode(FilterNode):
- def __init__(self, haml):
- FilterNode.__init__(self, haml)
+ def __init__(self, haml, options):
+ super(PlainFilterNode, self).__init__(haml, options)
+
self.empty_node = True
def _render(self):
@@ -541,7 +564,7 @@ def _render(self):
class JavascriptFilterNode(FilterNode):
def _render(self):
self.before = '\n'
@@ -550,7 +573,7 @@ def _render(self):
class CoffeeScriptFilterNode(FilterNode):
def _render(self):
self.before = '\n'
@@ -559,7 +582,7 @@ def _render(self):
class CssFilterNode(FilterNode):
def _render(self):
self.before = '\n'
@@ -568,7 +591,7 @@ def _render(self):
class StylusFilterNode(FilterNode):
def _render(self):
self.before = '\n'
diff --git a/hamlpy/template/loaders.py b/hamlpy/template/loaders.py
index 6a393e4..c7d16e4 100644
--- a/hamlpy/template/loaders.py
+++ b/hamlpy/template/loaders.py
@@ -10,10 +10,13 @@
from hamlpy.template.utils import get_django_template_loaders
# Get options from Django settings
-options_dict = {}
+options = {}
if hasattr(settings, 'HAMLPY_ATTR_WRAPPER'):
- options_dict.update(attr_wrapper=settings.HAMLPY_ATTR_WRAPPER)
+ options.update(attr_wrapper=settings.HAMLPY_ATTR_WRAPPER)
+
+if hasattr(settings, 'HAMLPY_DJANGO_INLINE_STYLE'):
+ options.update(django_inline_style=settings.HAMLPY_DJANGO_INLINE_STYLE)
def get_haml_loader(loader):
@@ -26,7 +29,7 @@ def get_contents(self, origin):
extension = _extension.lstrip('.')
if extension in hamlpy.VALID_EXTENSIONS:
- compiler = hamlpy.Compiler(options_dict=options_dict)
+ compiler = hamlpy.Compiler(options_dict=options)
return compiler.process(contents)
return contents
@@ -45,7 +48,7 @@ def load_template_source(self, template_name, *args, **kwargs):
except TemplateDoesNotExist:
pass
else:
- hamlParser = hamlpy.Compiler(options_dict=options_dict)
+ hamlParser = hamlpy.Compiler(options_dict=options)
html = hamlParser.process(haml_source)
return html, template_path
diff --git a/hamlpy/test/test_compiler.py b/hamlpy/test/test_compiler.py
index 4c126cf..11f4886 100755
--- a/hamlpy/test/test_compiler.py
+++ b/hamlpy/test/test_compiler.py
@@ -3,7 +3,7 @@
import unittest
-from hamlpy import hamlpy
+from hamlpy import hamlpy, nodes
class CompilerTest(unittest.TestCase):
@@ -104,6 +104,10 @@ def test_django_variables(self):
self._test("\\={name}, how are you?", "={name}, how are you?")
self._test("\\#{name}, how are you?", "#{name}, how are you?")
+ # can disable use of ={...} syntax
+ options = {'django_inline_style': False}
+ self._test("Dear ={title} #{name} href={{ var }}", "Dear ={title} {{ name }} href={{ var }}", options)
+
def test_django_tags(self):
# if/else
self._test('- if something\n %p hello\n- else\n %p goodbye',
@@ -165,6 +169,8 @@ def test_attr_wrapper(self):
''', options={'attr_wrapper': '"'})
def _test(self, haml, expected_html, options=None):
+ nodes._inline_variable_regexes = None # clear cached regexes
+
parser = hamlpy.Compiler(options)
result = parser.process(haml)
diff --git a/hamlpy/test/test_hamlnode.py b/hamlpy/test/test_hamlnode.py
index 594daed..813080f 100644
--- a/hamlpy/test/test_hamlnode.py
+++ b/hamlpy/test/test_hamlnode.py
@@ -3,50 +3,51 @@
import unittest
from hamlpy import nodes
+from hamlpy import hamlpy
class ElementNodeTest(unittest.TestCase):
def test_calculates_indentation_properly(self):
- no_indentation = nodes.ElementNode('%div')
+ no_indentation = nodes.ElementNode('%div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual(0, no_indentation.indentation)
- three_indentation = nodes.ElementNode(' %div')
+ three_indentation = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual(3, three_indentation.indentation)
- six_indentation = nodes.ElementNode(' %div')
+ six_indentation = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual(6, six_indentation.indentation)
def test_indents_tabs_properly(self):
- no_indentation = nodes.ElementNode('%div')
+ no_indentation = nodes.ElementNode('%div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual('', no_indentation.spaces)
- one_tab = nodes.HamlNode(' %div')
+ one_tab = nodes.HamlNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual('\t', one_tab.spaces)
- one_space = nodes.HamlNode(' %div')
+ one_space = nodes.HamlNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual(' ', one_space.spaces)
- three_tabs = nodes.HamlNode(' %div')
+ three_tabs = nodes.HamlNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual('\t\t\t', three_tabs.spaces)
- tab_space = nodes.HamlNode(' %div')
+ tab_space = nodes.HamlNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual('\t\t', tab_space.spaces)
- space_tab = nodes.HamlNode(' %div')
+ space_tab = nodes.HamlNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual(' ', space_tab.spaces)
def test_lines_are_always_stripped_of_whitespace(self):
- some_space = nodes.ElementNode(' %div')
+ some_space = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
self.assertEqual('%div', some_space.haml)
- lots_of_space = nodes.ElementNode(' %div ')
+ lots_of_space = nodes.ElementNode(' %div ', hamlpy.DEFAULT_OPTIONS)
self.assertEqual('%div', lots_of_space.haml)
def test_inserts_nodes_into_proper_tree_depth(self):
- no_indentation_node = nodes.ElementNode('%div')
- one_indentation_node = nodes.ElementNode(' %div')
- two_indentation_node = nodes.ElementNode(' %div')
- another_one_indentation_node = nodes.ElementNode(' %div')
+ no_indentation_node = nodes.ElementNode('%div', hamlpy.DEFAULT_OPTIONS)
+ one_indentation_node = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
+ two_indentation_node = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
+ another_one_indentation_node = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
no_indentation_node.add_node(one_indentation_node)
no_indentation_node.add_node(two_indentation_node)
@@ -57,10 +58,10 @@ def test_inserts_nodes_into_proper_tree_depth(self):
self.assertEqual(another_one_indentation_node, no_indentation_node.children[1])
def test_adds_multiple_nodes_to_one(self):
- start = nodes.ElementNode('%div')
- one = nodes.ElementNode(' %div')
- two = nodes.ElementNode(' %div')
- three = nodes.ElementNode(' %div')
+ start = nodes.ElementNode('%div', hamlpy.DEFAULT_OPTIONS)
+ one = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
+ two = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
+ three = nodes.ElementNode(' %div', hamlpy.DEFAULT_OPTIONS)
start.add_node(one)
start.add_node(two)
@@ -69,13 +70,13 @@ def test_adds_multiple_nodes_to_one(self):
self.assertEqual(3, len(start.children))
def test_node_parent_function(self):
- root = nodes.ElementNode('%div.a')
+ root = nodes.ElementNode('%div.a', hamlpy.DEFAULT_OPTIONS)
elements = [
- {'node': nodes.ElementNode(' %div.b'), 'expected_parent': 'root'},
- {'node': nodes.ElementNode(' %div.c'), 'expected_parent': 'root'},
- {'node': nodes.ElementNode(' %div.d'), 'expected_parent': 'elements[1]["node"]'},
- {'node': nodes.ElementNode(' %div.e'), 'expected_parent': 'elements[2]["node"]'},
- {'node': nodes.ElementNode(' %div.f'), 'expected_parent': 'root'},
+ {'node': nodes.ElementNode(' %div.b', hamlpy.DEFAULT_OPTIONS), 'expected_parent': 'root'},
+ {'node': nodes.ElementNode(' %div.c', hamlpy.DEFAULT_OPTIONS), 'expected_parent': 'root'},
+ {'node': nodes.ElementNode(' %div.d', hamlpy.DEFAULT_OPTIONS), 'expected_parent': 'elements[1]["node"]'},
+ {'node': nodes.ElementNode(' %div.e', hamlpy.DEFAULT_OPTIONS), 'expected_parent': 'elements[2]["node"]'},
+ {'node': nodes.ElementNode(' %div.f', hamlpy.DEFAULT_OPTIONS), 'expected_parent': 'root'},
]
for el in elements:
diff --git a/hamlpy/test/test_loader.py b/hamlpy/test/test_loader.py
index 32ac4b0..e9c1682 100644
--- a/hamlpy/test/test_loader.py
+++ b/hamlpy/test/test_loader.py
@@ -47,12 +47,16 @@ def test_compiler_settings(self, mock_compiler_class):
mock_compiler_class.assert_called_once_with(options_dict={})
mock_compiler_class.reset_mock()
- with override_settings(HAMLPY_ATTR_WRAPPER='"'):
+ with override_settings(HAMLPY_ATTR_WRAPPER='"', HAMLPY_DJANGO_INLINE_STYLE=False):
reload_module(hamlpy.template.loaders)
rendered = render_to_string('simple.hamlpy')
- mock_compiler_class.assert_called_once_with(options_dict={'attr_wrapper': '"'})
+ mock_compiler_class.assert_called_once_with(options_dict={
+ 'attr_wrapper': '"',
+ 'django_inline_style': False
+ })
+
assert '"someClass"' in rendered
def test_template_rendering(self):
diff --git a/hamlpy/test/test_node_factory.py b/hamlpy/test/test_node_factory.py
index e551f86..5bc1a26 100644
--- a/hamlpy/test/test_node_factory.py
+++ b/hamlpy/test/test_node_factory.py
@@ -3,68 +3,73 @@
import unittest
from hamlpy import nodes
+from hamlpy import hamlpy
class NodeFactoryTest(unittest.TestCase):
def test_creates_element_node_with_percent(self):
- node = nodes.create_node('%div')
+ node = self._create_node('%div')
assert isinstance(node, nodes.ElementNode)
- node = nodes.create_node(' %html')
+ node = self._create_node(' %html')
assert isinstance(node, nodes.ElementNode)
def test_creates_element_node_with_dot(self):
- node = nodes.create_node('.className')
+ node = self._create_node('.className')
assert isinstance(node, nodes.ElementNode)
- node = nodes.create_node(' .className')
+ node = self._create_node(' .className')
assert isinstance(node, nodes.ElementNode)
def test_creates_element_node_with_hash(self):
- node = nodes.create_node('#idName')
+ node = self._create_node('#idName')
assert isinstance(node, nodes.ElementNode)
- node = nodes.create_node(' #idName')
+ node = self._create_node(' #idName')
assert isinstance(node, nodes.ElementNode)
def test_creates_html_comment_node_with_front_slash(self):
- node = nodes.create_node('/ some Comment')
+ node = self._create_node('/ some Comment')
assert isinstance(node, nodes.CommentNode)
- node = nodes.create_node(' / some Comment')
+ node = self._create_node(' / some Comment')
assert isinstance(node, nodes.CommentNode)
def test_random_text_returns_haml_node(self):
- node = nodes.create_node('just some random text')
+ node = self._create_node('just some random text')
assert isinstance(node, nodes.HamlNode)
- node = nodes.create_node(' more random text')
+ node = self._create_node(' more random text')
assert isinstance(node, nodes.HamlNode)
def test_correct_symbol_creates_haml_comment(self):
- node = nodes.create_node('-# This is a haml comment')
+ node = self._create_node('-# This is a haml comment')
assert isinstance(node, nodes.HamlCommentNode)
def test_equals_symbol_creates_variable_node(self):
- node = nodes.create_node('= some.variable')
+ node = self._create_node('= some.variable')
assert isinstance(node, nodes.VariableNode)
def test_dash_symbol_creates_tag_node(self):
- node = nodes.create_node('- for something in somethings')
+ node = self._create_node('- for something in somethings')
assert isinstance(node, nodes.TagNode)
def test_backslash_symbol_creates_tag_node(self):
- node = nodes.create_node('\\= some.variable')
+ node = self._create_node('\\= some.variable')
assert isinstance(node, nodes.HamlNode)
- node = nodes.create_node(' \\= some.variable')
+ node = self._create_node(' \\= some.variable')
assert isinstance(node, nodes.HamlNode)
def test_python_creates_python_node(self):
- node = nodes.create_node(':python')
+ node = self._create_node(':python')
assert isinstance(node, nodes.PythonFilterNode)
def test_slash_with_if_creates_a_conditional_comment_node(self):
- node = nodes.create_node('/[if IE 5]')
+ node = self._create_node('/[if IE 5]')
assert isinstance(node, nodes.ConditionalCommentNode)
+
+ @staticmethod
+ def _create_node(line):
+ return nodes.create_node(line, hamlpy.DEFAULT_OPTIONS)
diff --git a/readme.md b/readme.md
index e16e66c..5b35ea6 100644
--- a/readme.md
+++ b/readme.md
@@ -116,7 +116,9 @@ TEMPLATE_LOADERS = (
Following values in Django settings affect haml processing:
- * `HAMLPY_ATTR_WRAPPER` -- The character that should wrap element attributes. This defaults to ' (an apostrophe).
+ * `HAMLPY_ATTR_WRAPPER` -- The character that should wrap element attributes. Defaults to `'` (an apostrophe).
+ * `HAMLPY_DJANGO_INLINE_STYLE` -- Whether to support `={...}` syntax for inline variables in addition to `#{...}`.
+ Defaults to `True`.
### Option 2: Watcher
diff --git a/reference.md b/reference.md
index ed03865..8262830 100644
--- a/reference.md
+++ b/reference.md
@@ -386,12 +386,12 @@ is compiled to:
```
-### Inline Django Variables: ={...}
+### Inline Django Variables: #{...}
-You can also use inline variables by surrounding the variable name with curly braces. For example:
+You can also use inline variables using the `#{...}` syntax. For example:
```haml
-Hello ={name}, how are you today?
+Hello #{name}, how are you today?
```
is compiled to
@@ -403,7 +403,7 @@ Hello {{ name }}, how are you today?
Inline variables can also be used in an element's attribute values. For example:
```haml
-%a{'title':'Hello ={name}, how are you?'} Hello
+%a{'title':'Hello #{name}, how are you?'} Hello
```
is compiled to:
@@ -415,16 +415,17 @@ is compiled to:
Inline variables can be escaped by placing a `\` before them. For example:
```haml
-Hello \={name}
+Hello \#{name}
```
-is compiled to
+is compiled to:
```htmldjango
-Hello ={name}
+Hello #{name}
```
-The Ruby style (`#{...}` rather than `={...}`) is also supported and the two can be used interchangeably.
+Django style `={...}` syntax is also optionally supported. If you are using the template loader
+then ensure `HAMLPY_DJANGO_INLINE_STYLE` is `True`, and the two syntaxes can then be used interchangeably.