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.