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, |
(...skipping 26 matching lines...) Expand all Loading... |
37 | 37 |
38 class CustomContext(object): | 38 class CustomContext(object): |
39 def get(self, key): | 39 def get(self, key): |
40 return 10 | 40 return 10 |
41 | 41 |
42 # Any time {{ }} is used, will fill it with 10. | 42 # Any time {{ }} is used, will fill it with 10. |
43 print(Handlebar('hello {{world}}').render(CustomContext()).text) | 43 print(Handlebar('hello {{world}}').render(CustomContext()).text) |
44 """ | 44 """ |
45 | 45 |
46 def _SafeStr(obj): | 46 def _SafeStr(obj): |
47 return obj if (type(obj) in [str, unicode]) else str(obj) | 47 return obj if isinstance(obj, basestring) else str(obj) |
48 | 48 |
49 class ParseException(Exception): | 49 class ParseException(Exception): |
50 """ Exception thrown while parsing the template. | 50 """ Exception thrown while parsing the template. |
51 """ | 51 """ |
52 def __init__(self, error, line): | 52 def __init__(self, error, line): |
53 Exception.__init__(self, "%s (line %s)" % (error, line.number)) | 53 Exception.__init__(self, "%s (line %s)" % (error, line.number)) |
54 | 54 |
55 class RenderResult(object): | 55 class RenderResult(object): |
56 """ Result of a render operation. | 56 """ Result of a render operation. |
57 """ | 57 """ |
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 """ | 385 """ |
386 def __init__(self, id, content): | 386 def __init__(self, id, content): |
387 DecoratorNode.__init__(self, content) | 387 DecoratorNode.__init__(self, content) |
388 self._id = id | 388 self._id = id |
389 | 389 |
390 def render(self, renderState): | 390 def render(self, renderState): |
391 value = self._id.resolve(renderState) | 391 value = self._id.resolve(renderState) |
392 if value == None: | 392 if value == None: |
393 return | 393 return |
394 | 394 |
395 type_ = type(value) | 395 if isinstance(value, list): |
396 if type_ == list: | |
397 for item in value: | 396 for item in value: |
398 renderState.localContexts.insert(0, item) | 397 renderState.localContexts.insert(0, item) |
399 self._content.render(renderState) | 398 self._content.render(renderState) |
400 renderState.localContexts.pop(0) | 399 renderState.localContexts.pop(0) |
401 elif type_ == dict: | 400 elif isinstance(value, dict): |
402 renderState.localContexts.insert(0, value) | 401 renderState.localContexts.insert(0, value) |
403 self._content.render(renderState) | 402 self._content.render(renderState) |
404 renderState.localContexts.pop(0) | 403 renderState.localContexts.pop(0) |
405 else: | 404 else: |
406 renderState.addError("{{#", self._id, | 405 renderState.addError("{{#", self._id, |
407 "}} cannot be rendered with a ", type_) | 406 "}} cannot be rendered with a ", type(value)) |
408 | 407 |
409 class VertedSectionNode(DecoratorNode): | 408 class VertedSectionNode(DecoratorNode): |
410 """ {{?foo}} ... {{/}} | 409 """ {{?foo}} ... {{/}} |
411 """ | 410 """ |
412 def __init__(self, id, content): | 411 def __init__(self, id, content): |
413 DecoratorNode.__init__(self, content) | 412 DecoratorNode.__init__(self, content) |
414 self._id = id | 413 self._id = id |
415 | 414 |
416 def render(self, renderState): | 415 def render(self, renderState): |
417 value = self._id.resolve(renderState.inSameContext().disableErrors()) | 416 value = self._id.resolve(renderState.inSameContext().disableErrors()) |
418 if _VertedSectionNodeShouldRender(value): | 417 if _VertedSectionNodeShouldRender(value): |
419 renderState.localContexts.insert(0, value) | 418 renderState.localContexts.insert(0, value) |
420 self._content.render(renderState) | 419 self._content.render(renderState) |
421 renderState.localContexts.pop(0) | 420 renderState.localContexts.pop(0) |
422 | 421 |
423 def _VertedSectionNodeShouldRender(value): | 422 def _VertedSectionNodeShouldRender(value): |
424 if value == None: | 423 if value == None: |
425 return False | 424 return False |
426 type_ = type(value) | 425 if isinstance(value, bool): |
427 if type_ == bool: | |
428 return value | 426 return value |
429 if type_ in [int, float]: | 427 if (isinstance(value, int) or |
| 428 isinstance(value, long) or |
| 429 isinstance(value, float)): |
430 return True | 430 return True |
431 if type_ in [str, unicode]: | 431 if isinstance(value, basestring): |
432 return True | 432 return True |
433 if type_ == list: | 433 if isinstance(value, list): |
434 return len(value) > 0 | 434 return len(value) > 0 |
435 if type_ == dict: | 435 if isinstance(value, dict): |
436 return True | 436 return True |
437 raise TypeError("Unhandled type: " + str(type_)) | 437 raise TypeError("Unhandled type %s" % type(value)) |
438 | 438 |
439 class InvertedSectionNode(DecoratorNode): | 439 class InvertedSectionNode(DecoratorNode): |
440 """ {{^foo}} ... {{/}} | 440 """ {{^foo}} ... {{/}} |
441 """ | 441 """ |
442 def __init__(self, id, content): | 442 def __init__(self, id, content): |
443 DecoratorNode.__init__(self, content) | 443 DecoratorNode.__init__(self, content) |
444 self._id = id | 444 self._id = id |
445 | 445 |
446 def render(self, renderState): | 446 def render(self, renderState): |
447 value = self._id.resolve(renderState.inSameContext().disableErrors()) | 447 value = self._id.resolve(renderState.inSameContext().disableErrors()) |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
581 return self.advance() | 581 return self.advance() |
582 | 582 |
583 def advanceOverNextString(self, excluded=''): | 583 def advanceOverNextString(self, excluded=''): |
584 buf = StringBuilder() | 584 buf = StringBuilder() |
585 while self.nextToken == Token.CHARACTER and \ | 585 while self.nextToken == Token.CHARACTER and \ |
586 excluded.find(self.nextContents) == -1: | 586 excluded.find(self.nextContents) == -1: |
587 buf.append(self.nextContents) | 587 buf.append(self.nextContents) |
588 self.advance() | 588 self.advance() |
589 return buf.toString() | 589 return buf.toString() |
590 | 590 |
| 591 def advanceToNextWhitespace(self): |
| 592 return self.advanceOverNextString(excluded=' \n\r\t') |
| 593 |
| 594 def skipWhitespace(self): |
| 595 while len(self.nextContents) > 0 and \ |
| 596 ' \n\r\t'.find(self.nextContents) >= 0: |
| 597 self.advance() |
| 598 |
591 class Handlebar(object): | 599 class Handlebar(object): |
592 """ A handlebar template. | 600 """ A handlebar template. |
593 """ | 601 """ |
594 def __init__(self, template): | 602 def __init__(self, template): |
595 self.source = template | 603 self.source = template |
596 tokens = TokenStream(template) | 604 tokens = TokenStream(template) |
597 self._topNode = self._parseSection(tokens) | 605 self._topNode = self._parseSection(tokens) |
598 if not self._topNode: | 606 if not self._topNode: |
599 raise ParseException("Template is empty", tokens.nextLine) | 607 raise ParseException("Template is empty", tokens.nextLine) |
600 if tokens.hasNext(): | 608 if tokens.hasNext(): |
(...skipping 12 matching lines...) Expand all Loading... |
613 startLine = tokens.nextLine | 621 startLine = tokens.nextLine |
614 string = tokens.advanceOverNextString() | 622 string = tokens.advanceOverNextString() |
615 nodes.append(StringNode(string, startLine, tokens.nextLine)) | 623 nodes.append(StringNode(string, startLine, tokens.nextLine)) |
616 elif token == Token.OPEN_VARIABLE or \ | 624 elif token == Token.OPEN_VARIABLE or \ |
617 token == Token.OPEN_UNESCAPED_VARIABLE or \ | 625 token == Token.OPEN_UNESCAPED_VARIABLE or \ |
618 token == Token.OPEN_START_JSON: | 626 token == Token.OPEN_START_JSON: |
619 id = self._openSectionOrTag(tokens) | 627 id = self._openSectionOrTag(tokens) |
620 nodes.append(token.clazz(id, tokens.nextLine)) | 628 nodes.append(token.clazz(id, tokens.nextLine)) |
621 elif token == Token.OPEN_START_PARTIAL: | 629 elif token == Token.OPEN_START_PARTIAL: |
622 tokens.advance() | 630 tokens.advance() |
623 id = Identifier(tokens.advanceOverNextString(excluded=' '), | 631 id = Identifier(tokens.advanceToNextWhitespace(), |
624 tokens.nextLine) | 632 tokens.nextLine) |
625 partialNode = PartialNode(id, tokens.nextLine) | 633 partialNode = PartialNode(id, tokens.nextLine) |
626 | 634 |
627 while tokens.nextToken == Token.CHARACTER: | 635 while tokens.nextToken == Token.CHARACTER: |
628 tokens.advance() | 636 tokens.skipWhitespace() |
629 key = tokens.advanceOverNextString(excluded=':') | 637 key = tokens.advanceOverNextString(excluded=':') |
630 tokens.advance() | 638 tokens.advance() |
631 partialNode.addArgument( | 639 partialNode.addArgument( |
632 key, | 640 key, |
633 Identifier(tokens.advanceOverNextString(excluded=' '), | 641 Identifier(tokens.advanceToNextWhitespace(), |
634 tokens.nextLine)) | 642 tokens.nextLine)) |
635 | 643 |
636 tokens.advanceOver(Token.CLOSE_MUSTACHE) | 644 tokens.advanceOver(Token.CLOSE_MUSTACHE) |
637 nodes.append(partialNode) | 645 nodes.append(partialNode) |
638 elif token == Token.OPEN_START_SECTION: | 646 elif token == Token.OPEN_START_SECTION: |
639 id = self._openSectionOrTag(tokens) | 647 id = self._openSectionOrTag(tokens) |
640 section = self._parseSection(tokens) | 648 section = self._parseSection(tokens) |
641 self._closeSection(tokens, id) | 649 self._closeSection(tokens, id) |
642 if section: | 650 if section: |
643 nodes.append(SectionNode(id, section)) | 651 nodes.append(SectionNode(id, section)) |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
739 def render(self, *contexts): | 747 def render(self, *contexts): |
740 """ Renders this template given a variable number of "contexts" to read | 748 """ Renders this template given a variable number of "contexts" to read |
741 out values from (such as those appearing in {{foo}}). | 749 out values from (such as those appearing in {{foo}}). |
742 """ | 750 """ |
743 globalContexts = [] | 751 globalContexts = [] |
744 for context in contexts: | 752 for context in contexts: |
745 globalContexts.append(context) | 753 globalContexts.append(context) |
746 renderState = RenderState(globalContexts, []) | 754 renderState = RenderState(globalContexts, []) |
747 self._topNode.render(renderState) | 755 self._topNode.render(renderState) |
748 return renderState.getResult() | 756 return renderState.getResult() |
OLD | NEW |