Source

vlastic / vlastic / rom / decorator.py

Full commit
"""Some utility decorators for resource-object mapping."""

from .. import http
from . import resource

__all__ = ["NegotiativeView", "VerbDecorator", "verb", "get", "post", "put",
           "delete"]

KEYWORDS_MIME_MAP = {
    "html": ("text/html", "application/xhtml+xml"),
    "json": ("application/json",),
    "xml": ("text/xml",),
    "text": ("text/plain",)
}


class NegotiativeView(dict):
    """Accept-aware higher-order view functor. Its constructor takes a
    {'mime/type': view...} map for negotiation.

    """

    def default_view(self, *args):
        """You can alter this method to set default view. Default view is
        chosen when the request doesn't contain Accept header.

        """
        for mime in KEYWORDS_MIME_MAP["html"]:
            if mime in self:
                return self[mime](*args)
        raise KeyError

    def __call__(self, context, request):
        accept = request.accept
        if accept:
            type = accept.best_match(self.keys())
            try:
                view = self[type]
            except KeyError:
                return http.NotAcceptableError()
        else:
            view = self.default_view
        try:
            return view(context, request)
        except TypeError:
            return view(context)


class VerbDecorator:
    """Creates a decorator to make a function to handle specific HTTP verb
    e.g. GET, POST.

    """

    def __init__(self, method):
        self.method = method.upper()

    def __call__(self, _=None, **kwtypes):
        """Accepts a view function and returns decorator which make given
        function to handle specific HTTP method through view function.

        """
        view = _
        if isinstance(view, dict):
            view = NegotiativeView(view)
        elif kwtypes:
            view = NegotiativeView()
            if hasattr(_, "__call__"):
                view.default_view = _
        if isinstance(view, NegotiativeView):
            for type, view4type in kwtypes.items():
                for mime in KEYWORDS_MIME_MAP[type]:
                    view[mime] = view4type
        def decorator(function):
            handler_attr = resource.METHOD_HANDLER_ATTRIBUTE_NAME
            if hasattr(function, handler_attr):
                getattr(function, handler_attr)[self.method] = view
            else:
                setattr(function, handler_attr, {self.method: view})
            return function
        return decorator


verb = VerbDecorator
get = VerbDecorator("GET")
post = VerbDecorator("POST")
put = VerbDecorator("PUT")
delete = VerbDecorator("DELETE")