thorn.decorators — Thorn 1.3.0 documentation

# Source code for thorn.decorators

    """

        thorn.webhook.decorators
        ========================

        Webhook decorators.

    """
    from __future__ import absolute_import, unicode_literals

    from itertools import chain

    from six import iteritems as items, itervalues as values
    from six.moves import UserDict

    class webhook_model(UserDict):
        """Decorates models to send webhooks based changes to that model.

        :keyword on_create: Event to dispatch whenever an instance of
            this model is created (``post_save``).
        :keyword on_change: Event to dispatch whenever an instance of
            this model is changed (``post_save``).
        :keyword on_delete: Event to dispatch whenever an instance of
            this model is deleted (``post_delete``).
        :keyword on_$event: Additional user defined events.,
        :keyword sender_field:
            Default field used as a sender for events, e.g. ``"account.user"``,
            will use ``instance.account.user``.

            Individual events can override the sender field user.
        :keyword reverse:
            A :class:`thorn.reverse.model_reverser` instance (or any callable
            taking an model instance as argument), that describes how to get
            the URL for an instance of this model.

            Individual events can override the reverser used.

        **Examples**

        Simple article model, where the URL reference is retrieved
        by ``reverse('article-detail', kwargs={'uuid': article.uuid})``:

        .. code-block:: python

            @webhook_model(
                on_create=ModelEvent('article.created'),
                on_change=ModelEvent('article.changed'),
                on_delete=ModelEvent('article.removed'),
                on_deactivate=ModelEvent(
                    'article.deactivate', deactivated__eq=True,
                )
                reverse=model_reverser('article-detail', uuid='uuid'),
            )
            class Article(models.Model):
                uuid = models.UUIDField()

        The URL may not actually exist after deletion, so maybe we want
        to point the reference to something else in that special case,
        like a category that can be reversed by doing
        ``reverse('category-detail', args=[article.category.name])``.

        We can do that by having the ``on_delete`` event override
        the reverser used for that event only:

        .. code-block:: python

            @webhook_model(
                on_create=ModelEvent('article.created'),
                on_change=ModelEvent('article.changed'),
                on_delete=ModelEvent(
                    'article.removed',
                    reverse=model_reverser('category-detail', 'category.name'),
                ),

                on_hipri_delete=ModelEvent(
                    'article.internal_delete', priority__gte=30.0,
                ).dispatches_on_delete(),

                reverse=model_reverser('article-detail', uuid='uuid'),
            )
            class Article(model.Model):
                uuid = models.UUIDField()
                category = models.ForeignKey('category')

        """

        def __init__(self,
                     on_create=None, on_change=None, on_delete=None,
                     reverse=None, sender_field=None, **kwargs):
            self.reverse = reverse
            self.sender_field = sender_field
            self.events = {}

            self.update_events(
                {k: v for k, v in items(kwargs) if k.startswith('on_')},
                on_create=on_create and on_create.dispatches_on_create(),
                on_change=on_change and on_change.dispatches_on_change(),
                on_delete=on_delete and on_delete.dispatches_on_delete(),
            )

        def update_events(self, events, **kwargs):
            self.events.update(self.connect_events(events, **kwargs))

        def connect_events(self, events, **kwargs):
            return {
                k: self.contribute_to_event(v)
                for k, v in chain(items(events), items(kwargs))
            }

        def contribute_to_event(self, event):
            if event:
                if event.reverse is None:
                    event.reverse = self.reverse
                event.sender_field = self.sender_field
            return event

        def __call__(self, model):
            [event.connect_model(model) for event in values(self.events) if event]
            model.webhook_events = self
            return model

        @property
        def data(self):
            # for UserDict
            return self.events