| OLD | NEW |
| 1 # Copyright 2012 Benjamin Kalman | 1 # Copyright 2012 Benjamin Kalman |
| 2 # | 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
| 6 # | 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and | 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. | 13 # limitations under the License. |
| 14 | 14 |
| 15 # TODO: New name, not "handlebar". | |
| 16 # TODO: Escaping control characters somehow. e.g. \{{, \{{-. | 15 # TODO: Escaping control characters somehow. e.g. \{{, \{{-. |
| 17 | 16 |
| 18 import json | 17 import json |
| 19 import re | 18 import re |
| 20 | 19 |
| 21 '''Handlebar templates are data binding templates more-than-loosely inspired by | 20 '''Motemplate templates are data binding templates more-than-loosely inspired by |
| 22 ctemplate. Use like: | 21 ctemplate. Use like: |
| 23 | 22 |
| 24 from handlebar import Handlebar | 23 from motemplate import Motemplate |
| 25 | 24 |
| 26 template = Handlebar('hello {{#foo bar/}} world') | 25 template = Motemplate('hello {{#foo bar/}} world') |
| 27 input = { | 26 input = { |
| 28 'foo': [ | 27 'foo': [ |
| 29 { 'bar': 1 }, | 28 { 'bar': 1 }, |
| 30 { 'bar': 2 }, | 29 { 'bar': 2 }, |
| 31 { 'bar': 3 } | 30 { 'bar': 3 } |
| 32 ] | 31 ] |
| 33 } | 32 } |
| 34 print(template.render(input).text) | 33 print(template.render(input).text) |
| 35 | 34 |
| 36 Handlebar will use get() on contexts to return values, so to create custom | 35 Motemplate will use get() on contexts to return values, so to create custom |
| 37 getters (for example, something that populates values lazily from keys), just | 36 getters (for example, something that populates values lazily from keys), just |
| 38 provide an object with a get() method. | 37 provide an object with a get() method. |
| 39 | 38 |
| 40 class CustomContext(object): | 39 class CustomContext(object): |
| 41 def get(self, key): | 40 def get(self, key): |
| 42 return 10 | 41 return 10 |
| 43 print(Handlebar('hello {{world}}').render(CustomContext()).text) | 42 print(Motemplate('hello {{world}}').render(CustomContext()).text) |
| 44 | 43 |
| 45 will print 'hello 10'. | 44 will print 'hello 10'. |
| 46 ''' | 45 ''' |
| 47 | 46 |
| 48 class ParseException(Exception): | 47 class ParseException(Exception): |
| 49 '''The exception thrown while parsing a template. | 48 '''The exception thrown while parsing a template. |
| 50 ''' | 49 ''' |
| 51 def __init__(self, error): | 50 def __init__(self, error): |
| 52 Exception.__init__(self, error) | 51 Exception.__init__(self, error) |
| 53 | 52 |
| (...skipping 587 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 641 def Render(self, render_state): | 640 def Render(self, render_state): |
| 642 value = render_state.contexts.Resolve(self._id.name) | 641 value = render_state.contexts.Resolve(self._id.name) |
| 643 if value is None: | 642 if value is None: |
| 644 render_state.AddResolutionError(self._id) | 643 render_state.AddResolutionError(self._id) |
| 645 return | 644 return |
| 646 render_state.text.Append(json.dumps(value, separators=(',',':'))) | 645 render_state.text.Append(json.dumps(value, separators=(',',':'))) |
| 647 | 646 |
| 648 def __repr__(self): | 647 def __repr__(self): |
| 649 return '{{*%s}}' % self._id | 648 return '{{*%s}}' % self._id |
| 650 | 649 |
| 651 # TODO: Better common model of _PartialNodeWithArguments, _PartialNodeInContext, | |
| 652 # and _PartialNode. | |
| 653 class _PartialNodeWithArguments(_DecoratorNode): | 650 class _PartialNodeWithArguments(_DecoratorNode): |
| 654 def __init__(self, partial, args): | 651 def __init__(self, partial, args): |
| 655 if isinstance(partial, Handlebar): | 652 if isinstance(partial, Motemplate): |
| 656 # Preserve any get() method that the caller has added. | 653 # Preserve any get() method that the caller has added. |
| 657 if hasattr(partial, 'get'): | 654 if hasattr(partial, 'get'): |
| 658 self.get = partial.get | 655 self.get = partial.get |
| 659 partial = partial._top_node | 656 partial = partial._top_node |
| 660 _DecoratorNode.__init__(self, partial) | 657 _DecoratorNode.__init__(self, partial) |
| 661 self._partial = partial | 658 self._partial = partial |
| 662 self._args = args | 659 self._args = args |
| 663 | 660 |
| 664 def Render(self, render_state): | 661 def Render(self, render_state): |
| 665 render_state.contexts.Scope(self._args, self._partial.Render, render_state) | 662 render_state.contexts.Scope(self._args, self._partial.Render, render_state) |
| 666 | 663 |
| 667 class _PartialNodeInContext(_DecoratorNode): | 664 class _PartialNodeInContext(_DecoratorNode): |
| 668 def __init__(self, partial, context): | 665 def __init__(self, partial, context): |
| 669 if isinstance(partial, Handlebar): | 666 if isinstance(partial, Motemplate): |
| 670 # Preserve any get() method that the caller has added. | 667 # Preserve any get() method that the caller has added. |
| 671 if hasattr(partial, 'get'): | 668 if hasattr(partial, 'get'): |
| 672 self.get = partial.get | 669 self.get = partial.get |
| 673 partial = partial._top_node | 670 partial = partial._top_node |
| 674 _DecoratorNode.__init__(self, partial) | 671 _DecoratorNode.__init__(self, partial) |
| 675 self._partial = partial | 672 self._partial = partial |
| 676 self._context = context | 673 self._context = context |
| 677 | 674 |
| 678 def Render(self, render_state): | 675 def Render(self, render_state): |
| 679 original_contexts = render_state.contexts | 676 original_contexts = render_state.contexts |
| (...skipping 20 matching lines...) Expand all Loading... |
| 700 | 697 |
| 701 @classmethod | 698 @classmethod |
| 702 def Inline(cls, id_): | 699 def Inline(cls, id_): |
| 703 return cls(None, id_, None) | 700 return cls(None, id_, None) |
| 704 | 701 |
| 705 def Render(self, render_state): | 702 def Render(self, render_state): |
| 706 value = render_state.contexts.Resolve(self._id.name) | 703 value = render_state.contexts.Resolve(self._id.name) |
| 707 if value is None: | 704 if value is None: |
| 708 render_state.AddResolutionError(self._id) | 705 render_state.AddResolutionError(self._id) |
| 709 return | 706 return |
| 710 if not isinstance(value, (Handlebar, _Node)): | 707 if not isinstance(value, (Motemplate, _Node)): |
| 711 render_state.AddResolutionError(self._id, description='not a partial') | 708 render_state.AddResolutionError(self._id, description='not a partial') |
| 712 return | 709 return |
| 713 | 710 |
| 714 if isinstance(value, Handlebar): | 711 if isinstance(value, Motemplate): |
| 715 node, name = value._top_node, value._name | 712 node, name = value._top_node, value._name |
| 716 else: | 713 else: |
| 717 node, name = value, None | 714 node, name = value, None |
| 718 | 715 |
| 719 partial_render_state = render_state.ForkPartial(name, self._id) | 716 partial_render_state = render_state.ForkPartial(name, self._id) |
| 720 | 717 |
| 721 arg_context = {} | 718 arg_context = {} |
| 722 if self._pass_through_id is not None: | 719 if self._pass_through_id is not None: |
| 723 context = render_state.contexts.Resolve(self._pass_through_id.name) | 720 context = render_state.contexts.Resolve(self._pass_through_id.name) |
| 724 if context is not None: | 721 if context is not None: |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 905 self.Advance() | 902 self.Advance() |
| 906 | 903 |
| 907 def __repr__(self): | 904 def __repr__(self): |
| 908 return '%s(next_token=%s, remainder=%s)' % (type(self).__name__, | 905 return '%s(next_token=%s, remainder=%s)' % (type(self).__name__, |
| 909 self.next_token, | 906 self.next_token, |
| 910 self._string[self._cursor:]) | 907 self._string[self._cursor:]) |
| 911 | 908 |
| 912 def __str__(self): | 909 def __str__(self): |
| 913 return repr(self) | 910 return repr(self) |
| 914 | 911 |
| 915 class Handlebar(object): | 912 class Motemplate(object): |
| 916 '''A handlebar template. | 913 '''A motemplate template. |
| 917 ''' | 914 ''' |
| 918 def __init__(self, template, name=None): | 915 def __init__(self, template, name=None): |
| 919 self.source = template | 916 self.source = template |
| 920 self._name = name | 917 self._name = name |
| 921 tokens = _TokenStream(template) | 918 tokens = _TokenStream(template) |
| 922 self._top_node = self._ParseSection(tokens) | 919 self._top_node = self._ParseSection(tokens) |
| 923 if not self._top_node: | 920 if not self._top_node: |
| 924 raise ParseException('Template is empty') | 921 raise ParseException('Template is empty') |
| 925 if tokens.HasNext(): | 922 if tokens.HasNext(): |
| 926 raise ParseException('There are still tokens remaining at %s, ' | 923 raise ParseException('There are still tokens remaining at %s, ' |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1149 return self.source == other.source and self._name == other._name | 1146 return self.source == other.source and self._name == other._name |
| 1150 | 1147 |
| 1151 def __ne__(self, other): | 1148 def __ne__(self, other): |
| 1152 return not (self == other) | 1149 return not (self == other) |
| 1153 | 1150 |
| 1154 def __repr__(self): | 1151 def __repr__(self): |
| 1155 return str('%s(%s)' % (type(self).__name__, self._top_node)) | 1152 return str('%s(%s)' % (type(self).__name__, self._top_node)) |
| 1156 | 1153 |
| 1157 def __str__(self): | 1154 def __str__(self): |
| 1158 return repr(self) | 1155 return repr(self) |
| OLD | NEW |