Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

Commit

Permalink
Merge pull request #106 from postatum/101820116_crud_hooks
Browse files Browse the repository at this point in the history
Dont fire events on particular auth views
  • Loading branch information
jstoiko committed Sep 10, 2015
2 parents 376cfba + dff6e37 commit 58fc701
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 15 deletions.
22 changes: 22 additions & 0 deletions docs/source/crud_events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ Utilities
**nefertari.events.AFTER_EVENTS**
Map of ``{view_method_name: EventClass}`` of "AFter" events. E.g. one of its elements is ``'index': AfterIndex``.

**nefertari.events.silent**
Decorator which marks view class or view method as "silent". Silent view classes and methods don't fire events. In the example below, view ``ItemsView`` won't fire any CRUD events. ``UsersView`` won't fire ``BeforeIndex`` and ``AfterIndex`` events but ``BeforeShow`` and ``AfterShow`` events will be fired.

.. code-block:: python
from nefertari import view, events
@events.silent
class ItemsView(view.BaseView):
...
class UsersView(view.BaseView):
@events.silent
def index(self):
...
def show(self):
...
Examples
Expand Down Expand Up @@ -157,3 +177,5 @@ API
.. autofunction:: nefertari.events.trigger_events

.. autofunction:: nefertari.events.subscribe_to_events

.. autofunction:: nefertari.events.silent
5 changes: 5 additions & 0 deletions nefertari/authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
JHTTPFound, JHTTPConflict, JHTTPUnauthorized, JHTTPNotFound, JHTTPOk,
JHTTPBadRequest)
from nefertari.view import BaseView
from nefertari import events


class TicketAuthViewMixin(object):
Expand Down Expand Up @@ -76,10 +77,12 @@ class TicketAuthLoginView(TicketAuthViewMixin, BaseView):
root.add('login', view='path.to.TicketAuthLoginView',
factory='nefertari.acl.AuthenticationACL')
"""
@events.silent
def create(self, *args, **kwargs):
return self.login(*args, **kwargs)


@events.silent
class TicketAuthLogoutView(TicketAuthViewMixin, BaseView):
""" Ticket auth logout view. Allows logout on GET and POST.
Expand Down Expand Up @@ -169,6 +172,7 @@ class TokenAuthClaimView(TokenAuthViewMixin, BaseView):
root.add('token', view='path.to.TokenAuthClaimView',
factory='nefertari.acl.AuthenticationACL')
"""
@events.silent
def create(self, *args, **kwargs):
return self.claim_token(*args, **kwargs)

Expand All @@ -181,5 +185,6 @@ class TokenAuthResetView(TokenAuthViewMixin, BaseView):
root.add('reset_token', view='path.to.TokenAuthResetView',
factory='nefertari.acl.AuthenticationACL')
"""
@events.silent
def create(self, *args, **kwargs):
return self.reset_token(*args, **kwargs)
48 changes: 34 additions & 14 deletions nefertari/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,23 +228,31 @@ def trigger_events(view_obj):
"""
from nefertari.utils import FieldData
request = view_obj.request
event_kwargs = {
'view': view_obj,
'model': view_obj.Model,
'fields': FieldData.from_dict(
view_obj._json_params,
view_obj.Model)
}
if isinstance(view_obj.context, view_obj.Model):
event_kwargs['instance'] = view_obj.context

before_event = BEFORE_EVENTS[request.action]
request.registry.notify(before_event(**event_kwargs))

view_method = getattr(view_obj, request.action)
do_trigger = not (
getattr(view_method, '_silent', False) or
getattr(view_obj, '_silent', False))

if do_trigger:
event_kwargs = {
'view': view_obj,
'model': view_obj.Model,
'fields': FieldData.from_dict(
view_obj._json_params,
view_obj.Model)
}
if isinstance(view_obj.context, view_obj.Model):
event_kwargs['instance'] = view_obj.context

before_event = BEFORE_EVENTS[request.action]
request.registry.notify(before_event(**event_kwargs))

yield

after_event = AFTER_EVENTS[request.action]
request.registry.notify(after_event(**event_kwargs))
if do_trigger:
after_event = AFTER_EVENTS[request.action]
request.registry.notify(after_event(**event_kwargs))


def subscribe_to_events(config, subscriber, events, model=None, field=None):
Expand All @@ -264,3 +272,15 @@ def subscribe_to_events(config, subscriber, events, model=None, field=None):

for evt in events:
config.add_subscriber(subscriber, evt, **kwargs)


def silent(obj):
""" Mark view method or class as "silent" so events won't be fired.
Should be used as decorator on view classes or methods.
:param obj: Any object that allows attributes assignment. Should be
either view method or view class.
"""
obj._silent = True
return obj
69 changes: 68 additions & 1 deletion tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ class A(object):
Model=A,
request=Mock(action='index'),
_json_params={'bar': 1},
context=ctx)
context=ctx,
_silent=False)
view.index._silent = False

with patch.dict(events.BEFORE_EVENTS, {'index': mock_before}):
with patch.dict(events.AFTER_EVENTS, {'index': mock_after}):
Expand All @@ -68,6 +70,58 @@ class A(object):
])
mock_from.assert_called_once_with({'bar': 1}, A)

@patch('nefertari.utils.FieldData.from_dict')
def test_trigger_events_silent_view(self, mock_from):
class A(object):
pass

mock_after = Mock()
mock_before = Mock()
ctx = A()
view = Mock(
Model=A,
request=Mock(action='index'),
_json_params={'bar': 1},
context=ctx,
_silent=True)
view.index._silent = False

with patch.dict(events.BEFORE_EVENTS, {'index': mock_before}):
with patch.dict(events.AFTER_EVENTS, {'index': mock_after}):
with events.trigger_events(view):
pass

assert not mock_after.called
assert not mock_before.called
assert not view.request.registry.notify.called
assert not mock_from.called

@patch('nefertari.utils.FieldData.from_dict')
def test_trigger_events_silent_view_method(self, mock_from):
class A(object):
pass

mock_after = Mock()
mock_before = Mock()
ctx = A()
view = Mock(
Model=A,
request=Mock(action='index'),
_json_params={'bar': 1},
context=ctx,
_silent=False)
view.index._silent = True

with patch.dict(events.BEFORE_EVENTS, {'index': mock_before}):
with patch.dict(events.AFTER_EVENTS, {'index': mock_after}):
with events.trigger_events(view):
pass

assert not mock_after.called
assert not mock_before.called
assert not view.request.registry.notify.called
assert not mock_from.called


class TestHelperFunctions(object):
def test_subscribe_to_events(self):
Expand All @@ -79,6 +133,19 @@ def test_subscribe_to_events(self):
call('foo', 2, model=3, field=4)
])

def test_silent_decorator(self):
@events.silent
def foo():
pass

assert foo._silent

@events.silent
class Foo(object):
pass

assert Foo._silent


class TestModelClassIs(object):
def test_wrong_class(self):
Expand Down

0 comments on commit 58fc701

Please sign in to comment.