mozilla-services/cornice — pull request #90 by thehunmonkgroup (2012-11-30 09:13:58+00:00)
Title: Allow service definitions across modules

Please forgive the long issue post -- this is a complicated issue, I will do my best to be brief!

As part of my work to get Cornice working with Pyramid's hybrid routing model (URL Dispatch + Traversal), I had a desire to be able to split out my view definitions into multiple modules. Because of the nature of traversal routing, one route can serve many many views, and it's impractical to stuff them all in one module.

I was quite disappointed to learn that Cornice doesn't support this kind of abstraction, despite quite a bit of trying, so I dug into the code to figure out why.

The included patches provide a working solution, but my feeling is that the right solution would involve a major code refactoring.

Venusian works by figuring out in which module the venusian.attach() method was called, marking the attached object, then during .scan() it looks for those marked objects and calls the attached callback when it finds one.

None of this is a problem when the Service definition and view decorators are in the same module -- the Service object is instantiated, the decorator wrapper adds the view definitions to the Service object, then during .scan(), Venusian finds the Service object in the module and fires the callback which adds the service definitions.

Things go remarkably haywire in this process once you start separating things into different modules however!

The first problem with the current architecture is that venusian.attach() is associated with the global Service object, so that's the only object that is marked for the attachment callback. You can't instantiate the same Service object more than once, it throws a configuration error for duplicate routes. Even finding a clever way to make the one global Service object available in multiple modules doesn't work, because the service definitions are managed by the same Service object, and those definitions get processed every time venusian.scan() triggers the callback which then calls cornice_add_service() -- if this happens any more than once, then you get Pyramid configuration errors for multiple registrations of the same view.

The attached patch takes these steps to try and solve the above problems:
* Move the venusian.attach() call out of the Service object instantiation, and into the decorator invocation. This is in line with how pyramid.view.view_config() works.
* Move the tracking of registered routes to the Service object -- this allows it to remain current across multiple uses of the instance.
* Track the last route definition that was added in the Service.definitions array. This allows the definition list to continue to be managed at the Service object level, while allowing subsequent invocations of cornice_add_service() to resume adding new definitions where it left off from the last call to cornice_add_service().
* Provide an extensive explanation of how to handle doing service definitions across files, with caveats explained.