Skip to content

Commit

Permalink
Merge branch 'master' into feature.multifileupload
Browse files Browse the repository at this point in the history
  • Loading branch information
reebalazs committed Nov 9, 2014
2 parents 4e79c1f + 2b7e706 commit c3c6e2c
Show file tree
Hide file tree
Showing 20 changed files with 410 additions and 222 deletions.
1 change: 1 addition & 0 deletions CHANGES.txt
@@ -1,5 +1,6 @@
Unreleased
----------

- Add AfterTransition event.
- Fixed #95 (When the scrolling causes an ajax request the selections are
lost.)
9 changes: 9 additions & 0 deletions docs/api.rst
Expand Up @@ -247,6 +247,10 @@ Other Helpers
:members:
:inherited-members:

.. autoclass:: AfterTransition
:members:
:inherited-members:

.. autoclass:: subscribe_added
:members:
:inherited-members:
Expand Down Expand Up @@ -279,6 +283,11 @@ Other Helpers
:members:
:inherited-members:

.. autoclass:: subscribe_after_transition
:members:
:inherited-members:


:mod:`substanced.evolution` API
--------------------------------

Expand Down
1 change: 1 addition & 0 deletions docs/configuration.rst
Expand Up @@ -80,6 +80,7 @@ In your configuration file (e.g. ``production.ini``), the

We thus need a ``relstorage.conf`` file::

%import relstorage
<zodb main>
<relstorage>
blob-dir ../var/blobs
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Expand Up @@ -25,12 +25,12 @@

install_requires = [
'pyramid>=1.5dev', # route_name argument to resource_url
'ZODB3',
'ZODB',
'hypatia>=0.2', # query objects have intersection/union methods
'venusian>=1.0a3', # pyramid wants this too (prefer_finals...)
'deform>=2.0a2', # asset spec in ZPTRendererFactory
'colander>=1.0a1', # subclassable schemanodes
'pyramid_zodbconn',
'pyramid_zodbconn>=0.6', # connection opened/closed events
'pyramid_chameleon',
'pyramid_mailer',
'cryptacular',
Expand Down
1 change: 1 addition & 0 deletions substanced/__init__.py
Expand Up @@ -22,6 +22,7 @@ def include(config): # pragma: no cover
config.include('.locking')
config.include('.audit')
config.include('.editable')
config.include('.db')
config.add_translation_dirs('locale/')

def scan(config): # pragma: no cover
Expand Down
4 changes: 2 additions & 2 deletions substanced/catalog/__init__.py
Expand Up @@ -639,7 +639,7 @@ def title(resource, default):
Or, a class::
def IndexViews(object):
class IndexViews(object):
def __init__(self, resource):
self.resource = resource
Expand All @@ -653,7 +653,7 @@ def __call__(self, default):
If an ``attr`` arg is supplied to ``add_indexview``, you can use a
different attribute of the class instad of ``__call__``::
def IndexViews(object):
class IndexViews(object):
def __init__(self, resource):
self.resource = resource
Expand Down
10 changes: 8 additions & 2 deletions substanced/catalog/system.py
Expand Up @@ -100,17 +100,23 @@ class SystemCatalogFactory(object):
Indexes text used for the Substance D folder contents filter box.
"""
# path index is not a "real" index (no writes happen when the index is
# updated with a new resource) so action mode is irrelevant
path = Path()

# name is MODE_ATCOMMIT for next-request folder contents consistency
name = Field()

# interfaces is MODE_ATCOMMIT because code which creates one may
# need to access it immediately.
# interfaces is MODE_ATCOMMIT because code which indexes something by its
# interface may need to access it immediately.
interfaces = Keyword()

# allowed index is not a "real" index (no writes happen when the index is
# updated with a new resource) so action mode is irrelevant
allowed = Allowed()

# results from a text search is considered deferrable until an arbitrary
# time in the future, so we use MODE_DEFERRED
text = Text(action_mode=MODE_DEFERRED)

# content_type is MODE_ATCOMMIT because code which creates one may
Expand Down
27 changes: 26 additions & 1 deletion substanced/db/__init__.py
@@ -1,5 +1,9 @@
import transaction
from pyramid_zodbconn import get_connection
from pyramid_zodbconn import (
get_connection,
ZODBConnectionOpened,
ZODBConnectionWillClose,
)
from substanced.evolution import mark_unfinished_as_finished

from ..stats import statsd_incr
Expand All @@ -24,3 +28,24 @@ def root_factory(request, t=transaction, g=get_connection,
statsd_incr('root_factory', rate=.1)
return zodb_root['app_root']

def connection_opened(event):
request = event.request
request._zodb_tx_counts = event.conn.getTransferCounts()

def connection_will_close(event, statsd_incr=statsd_incr):
# statsd_gauge is passed in above only for testing purposes
request = event.request
counts = getattr(request, '_zodb_tx_counts', None)
if counts is not None:
loads_before, stores_before = counts
loads_after, stores_after = event.conn.getTransferCounts()
loads = loads_after - loads_before
stores = stores_after - stores_before
if loads:
statsd_incr('zodb.loads', loads, registry=request.registry)
if stores:
statsd_incr('zodb.stores', stores, registry=request.registry)

def includeme(config):
config.add_subscriber(connection_opened, ZODBConnectionOpened)
config.add_subscriber(connection_will_close, ZODBConnectionWillClose)
75 changes: 75 additions & 0 deletions substanced/db/tests/test_init.py
Expand Up @@ -44,6 +44,63 @@ def test_with_app_root(self):
self.assertEqual(result, app_root)
self.assertFalse(txn.committed)

class Test_includeme(unittest.TestCase):
def test_it(self):
from .. import (
includeme,
connection_opened,
connection_will_close,
ZODBConnectionOpened,
ZODBConnectionWillClose,
)
config = DummyConfig()
includeme(config)
self.assertEqual(
config.subscriptions,
[(connection_opened, ZODBConnectionOpened),
(connection_will_close, ZODBConnectionWillClose),
]
)

class Test_connection_opened(unittest.TestCase):
def test_it(self):
from .. import connection_opened
event = DummyEvent()
connection_opened(event)
self.assertEqual(event.request._zodb_tx_counts, (0,0))

class Test_connection_will_close(unittest.TestCase):
def _callFUT(self, event, statsd_incr):
from .. import connection_will_close
return connection_will_close(event, statsd_incr)

def test_no_tx_counts(self):
event = DummyEvent()
result = self._callFUT(event, None)
self.assertEqual(result, None) # doesnt fail

def test_with_postitive_tx_counts(self):
event = DummyEvent(5,5)
event.request._zodb_tx_counts = (1, 1)
L = []
def statsd_incr(name, num, registry=None):
L.append((name, num))
self._callFUT(event, statsd_incr)
self.assertEqual(
L,
[('zodb.loads', 4), ('zodb.stores', 4)]
)

def test_with_zero_tx_counts(self):
event = DummyEvent(1,1)
event.request._zodb_tx_counts = (1, 1)
L = []
self._callFUT(event, None)
self.assertEqual(
L,
[]
)

class DummyTransaction(object):
committed = False
savepointed = False
Expand Down Expand Up @@ -80,3 +137,21 @@ class DummyRegistry(object):
def notify(self, event):
self.event = event

class DummyConfig(object):
def __init__(self):
self.subscriptions = []
def add_subscriber(self, fn, event_type):
self.subscriptions.append((fn, event_type))

class DummyConnection(object):
def __init__(self, loads, stores):
self.loads = loads
self.stores = stores

def getTransferCounts(self):
return (self.loads, self.stores)

class DummyEvent(object):
def __init__(self, loads=0, stores=0):
self.request = testing.DummyRequest()
self.conn = DummyConnection(loads, stores)
23 changes: 19 additions & 4 deletions substanced/event/__init__.py
Expand Up @@ -19,13 +19,14 @@
IContentCreated,
ILoggedIn,
IRootAdded,
IAfterTransition,
)

from ..util import find_objectmap

class _ObjectEvent(object):
pass


@implementer(IObjectAdded)
class ObjectAdded(_ObjectEvent):
Expand Down Expand Up @@ -143,7 +144,16 @@ def __init__(self, login, user, context, request):
class RootAdded(object):
def __init__(self, object):
self.object = object


@implementer(IAfterTransition)
class AfterTransition(object):
""" Event sent after any workflow transition happens """
def __init__(self, object, old_state, new_state, transition):
self.object = object
self.old_state = old_state
self.new_state = new_state
self.transition = transition

# subscriber decorators, e.g.
# @subscribe_added(MyContent)
# def foo(event):
Expand Down Expand Up @@ -253,6 +263,11 @@ class subscribe_root_added(_SimpleSubscriber):
has a database connection """
event = IRootAdded

class subscribe_after_transition(_SimpleSubscriber):
""" Decorator for registering an event listener for when a transition has
been done on an object"""
event = IAfterTransition

def add_content_subscriber(config, subscriber, iface=None, **predicates):
""" Configurator directive that works like Pyramid's ``add_subscriber``,
except it wraps the subscriber in something that first adds the
Expand Down Expand Up @@ -283,7 +298,7 @@ def __call__(self, event, *arg):
# XXX *arg can go away once a Pyramid 1.4 final is out (or if used
# against Pyramid 1.4b1+)
return self.registry.content.istype(event.object, self.val)

def includeme(config): # pragma: no cover
config.add_directive('add_content_subscriber', add_content_subscriber)
config.add_subscriber_predicate('content_type', _ContentTypePredicate)
7 changes: 7 additions & 0 deletions substanced/interfaces.py
Expand Up @@ -284,6 +284,13 @@ class IRootAdded(Interface):
the database as its ``_p_jar`` attribute. """
object = Attribute('The root object')

class IAfterTransition(Interface):
""" An event type sent after a transition has been done """
object = Attribute('The object on which the transition has been done')
initial_state = Attribute('The initial state of the object')
new_state = Attribute('The new state of the object')
transition = Attribute('The transition name')

#
# subtanced.evolution APIs
#
Expand Down
Binary file modified substanced/sdi/static/fonts/glyphicons-halflings-regular.eot
Binary file not shown.

0 comments on commit c3c6e2c

Please sign in to comment.