You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@bloodhound.apache.org by Andrej Golcov <an...@digiverse.si> on 2013/02/25 18:19:17 UTC
Re: svn commit: r1449780 - in /incubator/bloodhound/trunk/trac/trac/ticket:
api.py model.py tests/model.py
As previously discussed on the mailing list, a new generic interface
IResourceChangeListener should be introduced.
The commit introduces the interface and provides the first usage of
the interface for Component resource.
I would kindly ask community to review the change and provide feedback.
For the next step, I plan to add usage of IResourceChangeListener by
other resources such as ticket, milestone, version and other
AbstractEnum based resources.
Cheers, Andrej
On 25 February 2013 18:10, <an...@apache.org> wrote:
> Author: andrej
> Date: Mon Feb 25 17:10:42 2013
> New Revision: 1449780
>
> URL: http://svn.apache.org/r1449780
> Log:
> patch Trac to enable genereic IResourceChangeListener interfce required for support of consistancy between DB and index (#394, #411)
>
> Modified:
> incubator/bloodhound/trunk/trac/trac/ticket/api.py
> incubator/bloodhound/trunk/trac/trac/ticket/model.py
> incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py
>
> Modified: incubator/bloodhound/trunk/trac/trac/ticket/api.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/api.py?rev=1449780&r1=1449779&r2=1449780&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/trac/trac/ticket/api.py (original)
> +++ incubator/bloodhound/trunk/trac/trac/ticket/api.py Mon Feb 25 17:10:42 2013
> @@ -156,6 +156,31 @@ class IMilestoneChangeListener(Interface
> def milestone_deleted(milestone):
> """Called when a milestone is deleted."""
>
> +class IResourceChangeListener(Interface):
> + """Extension point interface for components that require notification
> + when resources are created, modified, or deleted.
> +
> + 'resource' instance of the a resource e.g. ticket, milestone etc.
> + 'context' action context, may contain author, comment etc. Context
> + content depends on a resource type.
> + """
> +
> + def resource_created(resource, context):
> + """
> + Called when a resource is created.
> + """
> +
> + def resource_changed(resource, old_values, context):
> + """Called when a resource is modified.
> +
> + `old_values` is a dictionary containing the previous values of the
> + resource properties that changed. Properties are specific for resource
> + type.
> + """
> +
> + def resource_deleted(resource, context):
> + """Called when a resource is deleted."""
> +
> class ITicketFieldProvider(Interface):
> """Extension point interface for components that provide fields for the
> ticket system."""
> @@ -193,6 +218,7 @@ class TicketSystem(Component):
> ticket_field_providers = ExtensionPoint(ITicketFieldProvider)
> change_listeners = ExtensionPoint(ITicketChangeListener)
> milestone_change_listeners = ExtensionPoint(IMilestoneChangeListener)
> + resource_change_listeners = ExtensionPoint(IResourceChangeListener)
>
> ticket_custom_section = ConfigSection('ticket-custom',
> """In this section, you can define additional fields for tickets. See
>
> Modified: incubator/bloodhound/trunk/trac/trac/ticket/model.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/model.py?rev=1449780&r1=1449779&r2=1449780&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/trac/trac/ticket/model.py (original)
> +++ incubator/bloodhound/trunk/trac/trac/ticket/model.py Mon Feb 25 17:10:42 2013
> @@ -844,9 +844,12 @@ class Component(object):
> with self.env.db_transaction as db:
> self.env.log.info("Deleting component %s", self.name)
> db("DELETE FROM component WHERE name=%s", (self.name,))
> - self.name = self._old_name = None
> TicketSystem(self.env).reset_ticket_fields()
>
> + for listener in TicketSystem(self.env).resource_change_listeners:
> + listener.resource_deleted(self)
> + self.name = self._old_name = None
> +
> def insert(self, db=None):
> """Insert a new component.
>
> @@ -866,6 +869,9 @@ class Component(object):
> self._old_name = self.name
> TicketSystem(self.env).reset_ticket_fields()
>
> + for listener in TicketSystem(self.env).resource_change_listeners:
> + listener.resource_created(self)
> +
> def update(self, db=None):
> """Update the component.
>
> @@ -877,6 +883,7 @@ class Component(object):
> if not self.name:
> raise TracError(_("Invalid component name."))
>
> + old_name = self._old_name
> with self.env.db_transaction as db:
> self.env.log.info("Updating component '%s'", self.name)
> db("""UPDATE component SET name=%s,owner=%s, description=%s
> @@ -890,6 +897,10 @@ class Component(object):
> self._old_name = self.name
> TicketSystem(self.env).reset_ticket_fields()
>
> + old_values = dict(name=old_name)
> + for listener in TicketSystem(self.env).resource_change_listeners:
> + listener.resource_changed(self, old_values)
> +
> @classmethod
> def select(cls, env, db=None):
> """
>
> Modified: incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py?rev=1449780&r1=1449779&r2=1449780&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py (original)
> +++ incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py Mon Feb 25 17:10:42 2013
> @@ -15,7 +15,8 @@ from trac.ticket.model import (
> Ticket, Component, Milestone, Priority, Type, Version
> )
> from trac.ticket.api import (
> - IMilestoneChangeListener, ITicketChangeListener, TicketSystem
> + IMilestoneChangeListener, ITicketChangeListener, TicketSystem,
> + IResourceChangeListener,
> )
> from trac.test import EnvironmentStub
> from trac.util.datefmt import from_utimestamp, to_utimestamp, utc
> @@ -1005,6 +1006,27 @@ class MilestoneTestCase(unittest.TestCas
> self.assertEqual('deleted', listener.action)
> self.assertEqual(milestone, listener.milestone)
>
> +class TestResourceChangeListener(core.Component):
> + implements(IResourceChangeListener)
> +
> + def callback(self, action, resource, old_values = None):
> + pass
> +
> + def resource_created(self, resource):
> + self.action = "created"
> + self.resource = resource
> + self.callback(self.action, self.resource)
> +
> + def resource_changed(self, resource, old_values):
> + self.action = "changed"
> + self.resource = resource
> + self.old_values = old_values
> + self.callback(self.action, self.resource, old_values=self.old_values)
> +
> + def resource_deleted(self, resource):
> + self.action = "deleted"
> + self.resource = resource
> + self.callback(self.action, self.resource)
>
> class ComponentTestCase(unittest.TestCase):
>
> @@ -1041,6 +1063,49 @@ class ComponentTestCase(unittest.TestCas
> self.assertEqual([('Test', 'joe', None)], self.env.db_query(
> "SELECT name, owner, description FROM component WHERE name='Test'"))
>
> + def test_change_listener_created(self):
> + listener = TestResourceChangeListener(self.env)
> + self._create_component(name='Component 1')
> + self.assertEqual('created', listener.action)
> + self.assertIsInstance(listener.resource, Component)
> + self.assertEqual('Component 1', listener.resource.name)
> +
> + def test_change_listener_changed(self):
> + listener = TestResourceChangeListener(self.env)
> + component = self._create_component(name='Component 1')
> + component.name = 'Component 2'
> + component.update()
> + self.assertEqual('changed', listener.action)
> + self.assertIsInstance(listener.resource, Component)
> + self.assertEqual('Component 2', listener.resource.name)
> + self.assertEqual("Component 1" ,listener.old_values["name"])
> +
> + def test_change_listener_deleted(self):
> + listener = TestResourceChangeListener(self.env)
> +
> + #component.name property is set to None during delete operation
> + #We need mechanism to remember component name
> + listener.callback = self.listener_callback
> +
> + component = self._create_component(name='Component 1')
> + component.delete()
> + self.assertEqual('deleted', listener.action)
> + self.assertIsInstance(listener.resource, Component)
> + self.assertEqual('Component 1', self.resource_name)
> +
> + def listener_callback(self, action, resource, old_values = None):
> + self.resource_name = resource.name
> +
> + def _create_component(self, name, description = None, owner=None):
> + component = Component(self.env)
> + component.name = name
> + if description is None:
> + component.description = description
> + if owner is None:
> + component.owner = owner
> + component.insert()
> + return component
> +
> class VersionTestCase(unittest.TestCase):
>
> def setUp(self):
>
>
Re: svn commit: r1449780 - in /incubator/bloodhound/trunk/trac/trac/ticket:
api.py model.py tests/model.py
Posted by Olemis Lang <ol...@gmail.com>.
On 2/26/13, Andrej Golcov <an...@digiverse.si> wrote:
>>> Finally , it'd be nice to keep changes to be backwards compatible with
>>> existing interfaces . I can suggest a few patches to work towards that
>>> goal ... later .
>>
>> I hope we all feel that this is a requirement, otherwise we break
>> compatibility with a number of plugins.
> Sure, I haven't mentioned this since consider the requirement as "must
> have".
>
>>> > $ grep -r class trac | grep -v ".js" | grep Manipulator | more
> I would pospone I*Manipulator interface changing until we really need
> it and rather concentrate on delivery of top features we want to have
> in the nearest releases.
>
Of course !
I just mentioned we should keep this under our pillow . Initial work
should focus on I*Listeners
;)
--
Regards,
Olemis.
Re: svn commit: r1449780 - in /incubator/bloodhound/trunk/trac/trac/ticket:
api.py model.py tests/model.py
Posted by Andrej Golcov <an...@digiverse.si>.
>> Finally , it'd be nice to keep changes to be backwards compatible with
>> existing interfaces . I can suggest a few patches to work towards that
>> goal ... later .
>
> I hope we all feel that this is a requirement, otherwise we break
> compatibility with a number of plugins.
Sure, I haven't mentioned this since consider the requirement as "must have".
>> > $ grep -r class trac | grep -v ".js" | grep Manipulator | more
I would pospone I*Manipulator interface changing until we really need
it and rather concentrate on delivery of top features we want to have
in the nearest releases.
Thanks for comments.
Cheers, Andrej
Re: svn commit: r1449780 - in /incubator/bloodhound/trunk/trac/trac/ticket:
api.py model.py tests/model.py
Posted by Ryan Ollos <ry...@wandisco.com>.
On Mon, Feb 25, 2013 at 11:11 PM, Olemis Lang <ol...@gmail.com> wrote:
> [...]
> Finally , it'd be nice to keep changes to be backwards compatible with
> existing interfaces . I can suggest a few patches to work towards that
> goal ... later .
I hope we all feel that this is a requirement, otherwise we break
compatibility with a number of plugins.
Re: svn commit: r1449780 - in /incubator/bloodhound/trunk/trac/trac/ticket:
api.py model.py tests/model.py
Posted by Olemis Lang <ol...@gmail.com>.
On 2/25/13, Andrej Golcov <an...@digiverse.si> wrote:
> As previously discussed on the mailing list, a new generic interface
> IResourceChangeListener should be introduced.
Good !
> The commit introduces the interface and provides the first usage of
> the interface for Component resource.
> I would kindly ask community to review the change and provide feedback.
>
I will provide further feedback once I have a few minutes to take a
look and see the details . The broad scope of this work should cover
replacement for the following existing I*Listener interfaces :
{{{
#!sh
$ grep -r class trac | grep -v ".js" | grep Interface | grep Listener | more
trac/attachment.py:class IAttachmentChangeListener(Interface):
trac/ticket/api.py:class ITicketChangeListener(Interface):
trac/ticket/api.py:class IMilestoneChangeListener(Interface):
trac/versioncontrol/api.py:class IRepositoryChangeListener(Interface):
trac/wiki/api.py:class IWikiChangeListener(Interface):
}}}
So a superset of these will be needed . Notice that there are a few
exotic callbacks (e.g. attachment_reparented) and resource-specific
prefixes should be discarded .
Besides it would be the foundation to replace these ones as well .
{{{
#!sh
$ grep -r class trac | grep -v ".js" | grep Manipulator | more
trac/attachment.py:class IAttachmentManipulator(Interface):
trac/ticket/api.py:class ITicketManipulator(Interface):
trac/wiki/api.py:class IWikiPageManipulator(Interface):
}}}
Finally , it'd be nice to keep changes to be backwards compatible with
existing interfaces . I can suggest a few patches to work towards that
goal ... later .
> For the next step, I plan to add usage of IResourceChangeListener by
> other resources such as ticket, milestone, version and other
> AbstractEnum based resources.
>
Look forward to see this work .
;)
--
Regards,
Olemis.