123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- from operator import attrgetter
-
- from django.template import Node
-
- from classytags.blocks import BlockDefinition
- from classytags.compat import compat_basestring
- from classytags.parser import Parser
- from classytags.utils import StructuredOptions
- from classytags.utils import get_default_name
-
-
- class Options(object):
- """
- Option class holding the arguments of a tag.
- """
- def __init__(self, *options, **kwargs):
- self._options = options
- self._kwargs = kwargs
- self.options = {}
- self.raw_options = options
- self.breakpoints = []
- self.combined_breakpoints = {}
- current_breakpoint = None
- last = None
- self.options[current_breakpoint] = []
- self.all_argument_names = []
- for value in options:
- if isinstance(value, compat_basestring):
- if isinstance(last, compat_basestring):
- self.combined_breakpoints[last] = value
- self.breakpoints.append(value)
- current_breakpoint = value
- self.options[current_breakpoint] = []
- else:
- self.options[current_breakpoint].append(value)
- self.all_argument_names.append(value.name)
- last = value
- self.blocks = []
- for block in kwargs.get('blocks', []):
- if isinstance(block, BlockDefinition):
- block_definition = block
- elif isinstance(block, compat_basestring):
- block_definition = BlockDefinition(block, block)
- else:
- block_definition = BlockDefinition(block[1], block[0])
- block_definition.validate(self)
- self.blocks.append(block_definition)
- if 'parser_class' in kwargs:
- self.parser_class = kwargs['parser_class']
- else:
- self.parser_class = Parser
-
- def __repr__(self):
- bits = list(map(repr, self.options[None]))
- for breakpoint in self.breakpoints:
- bits.append(breakpoint)
- for option in self.options[breakpoint]:
- bits.append(repr(option))
- options = ','.join(bits)
- if self.blocks:
- blocks = ';%s' % ','.join(map(attrgetter('alias'), self.blocks))
- else: # pragma: no cover
- blocks = ''
- return '<Options:%s%s>' % (options, blocks)
-
- def __add__(self, other):
- if not isinstance(other, Options):
- raise TypeError("Cannot add Options to non-Options object")
- if self.blocks and other.blocks:
- raise ValueError(
- "Cannot add two Options objects if both objects define blocks"
- )
- if self.parser_class is not other.parser_class:
- raise ValueError(
- "Cannot add two Options objects with different parser classes"
- )
- full_options = self._options + other._options
- full_kwargs = {
- 'parser_class': self.parser_class
- }
- if self._kwargs.get('blocks', False):
- full_kwargs['blocks'] = self._kwargs['blocks']
- elif other._kwargs.get('blocks', False):
- full_kwargs['blocks'] = other._kwargs['blocks']
- return Options(*full_options, **full_kwargs)
-
- def get_parser_class(self):
- return self.parser_class
-
- def bootstrap(self):
- """
- Bootstrap this options
- """
- return StructuredOptions(
- self.options,
- self.breakpoints,
- self.blocks,
- self.combined_breakpoints
- )
-
- def parse(self, parser, tokens):
- """
- Parse template tokens into a dictionary
- """
- argument_parser_class = self.get_parser_class()
- argument_parser = argument_parser_class(self)
- return argument_parser.parse(parser, tokens)
-
-
- class TagMeta(type):
- """
- Metaclass for the Tag class that set's the name attribute onto the class
- and a _decorated_function pseudo-function which is used by Django's
- template system to get the tag name.
- """
- def __new__(cls, name, bases, attrs):
- parents = [base for base in bases if isinstance(base, TagMeta)]
- if not parents:
- return super(TagMeta, cls).__new__(cls, name, bases, attrs)
- tag_name = str(attrs.get('name', get_default_name(name)))
-
- def fake_func():
- pass # pragma: no cover
-
- fake_func.__name__ = tag_name
- attrs['_decorated_function'] = fake_func
- attrs['name'] = str(tag_name)
- return super(TagMeta, cls).__new__(cls, name, bases, attrs)
-
-
- class Tag(TagMeta('TagMeta', (Node,), {})):
- """
- Main Tag class.
- """
- options = Options()
- name = None
-
- def __init__(self, parser, tokens):
- self.kwargs, self.blocks = self.options.parse(parser, tokens)
- self.child_nodelists = []
- for key, value in self.blocks.items():
- setattr(self, key, value)
- self.child_nodelists.append(key)
-
- def render(self, context):
- """
- INTERNAL method to prepare rendering
- Usually you should not override this method, but rather use render_tag.
- """
- items = self.kwargs.items()
- kwargs = dict([(key, value.resolve(context)) for key, value in items])
- kwargs.update(self.blocks)
- return self.render_tag(context, **kwargs)
-
- def render_tag(self, context, **kwargs):
- """
- The method you should override in your custom tags
- """
- raise NotImplementedError
-
- def __repr__(self):
- return '<Tag: %s>' % self.name
|