import sys

class BeforeFilter(object):
    """
    Middleware component for performing actions before
    calling views.

    To use, define BEFORE_FILTER in your application's views.py.
    BEFORE_FILTER should be a dictionary mapping a filter function
    to a list/tuple of view functions::

        BEFORE_FILTER = {'check_trailing_slash': ('index',
                                                  'detail')}

    To have *all* view functions filtered, simply leave the tuple
    blank::

        BEFORE_FILTER = {'authenticate': ()}

    To exclude a view from being filtered prefix it with dash::

        BEFORE_FILTER = {'authenticate': ('-index',
                                          '-detail')}

    If you require filters to be applied in a specific order, use Django's
    SortedDict::

        from django.utils.datastructures import SortedDict

        BEFORE_FILTER = SortedDict()
        BEFORE_FILTER['authenticate'] = ('-index', '-detail')
        BEFORE_FILTER['get_messages'] = ()

    """
    def __init__(self):
        self.setting = 'BEFORE_FILTER'
        self.filters = []

    def _call_filters(self, request, view, args, kwargs):
        response = None
        for f in self.filters:
            response = f(request, view, args, kwargs)
            if response and response.has_header('location'):
                return response
        self.filters = []
        return response

    def process_view(self, request, view, args, kwargs):
        module = sys.modules[view.__module__]
        if hasattr(module, self.setting):
            for func, views in getattr(module, self.setting).items():
                exclude = '-%s' % view.func_name
                if not views or view.func_name in views or views and \
                    exclude not in views:
                    if hasattr(module, func):
                        if getattr(module, func) not in self.filters:
                            self.filters.append(getattr(module, func))
        if self.filters:
            return self._call_filters(request, view, args, kwargs)
        return None