Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1f4a8c9
Showing
1 changed file
with
291 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
############################################################################## | ||
# | ||
# Copyright (c) 2004 Zope Corporation and Contributors. | ||
# All Rights Reserved. | ||
# | ||
# This software is subject to the provisions of the Zope Public License, | ||
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. | ||
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | ||
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | ||
# FOR A PARTICULAR PURPOSE. | ||
# | ||
############################################################################## | ||
"""UI for browsing database schema managers | ||
$Id$ | ||
""" | ||
|
||
from zope.app import zapi | ||
from zope.app.generations.interfaces import ISchemaManager | ||
from zope.app.generations.generations import generations_key, Context | ||
from zope.app.i18n import ZopeMessageIDFactory as _ | ||
|
||
request_key_format = "evolve-app-%s" | ||
|
||
class Managers: | ||
|
||
def __init__(self, context, request): | ||
self.context = context | ||
self.request = request | ||
|
||
def _getdb(self): | ||
# TODO: There needs to be a better api for this | ||
return self.request.publication.db | ||
|
||
def evolve(self): | ||
"""Perform a requested evolution | ||
This method needs to use the component architecture, so | ||
we'll set it up: | ||
>>> from zope.app.tests.placelesssetup import setUp, tearDown | ||
>>> setUp() | ||
We also need a test request: | ||
>>> from zope.publisher.browser import TestRequest | ||
>>> request = TestRequest() | ||
We also need to give it a publication with a database: | ||
>>> class Publication: | ||
... pass | ||
>>> request.setPublication(Publication()) | ||
>>> from ZODB.tests.util import DB | ||
>>> db = DB() | ||
>>> request.publication.db = db | ||
We need to define some schema managers. We'll define two | ||
using the demo package: | ||
>>> from zope.app.generations.generations import SchemaManager | ||
>>> from zope.app.tests import ztapi | ||
>>> app1 = SchemaManager(0, 1, 'zope.app.generations.demo') | ||
>>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1') | ||
>>> app2 = SchemaManager(0, 0, 'zope.app.generations.demo') | ||
>>> ztapi.provideUtility(ISchemaManager, app2, 'foo.app2') | ||
And we need to record some data for them in the database. | ||
>>> from zope.app.generations.generations import evolve | ||
>>> evolve(db) | ||
This sets up the data and actually evolves app1: | ||
>>> conn = db.open() | ||
>>> conn.root()[generations_key]['foo.app1'] | ||
1 | ||
>>> conn.root()[generations_key]['foo.app2'] | ||
0 | ||
To evolve a data base schema, the user clicks on a submit | ||
button. If they click on the button for add1, a item will | ||
be added to the request, which we simulate: | ||
>>> request.form['evolve-app-foo.app1'] = 'evolve' | ||
We'll also increase the generation of app1: | ||
>>> app1.generation = 2 | ||
Now we can create our view: | ||
>>> view = Managers(None, request) | ||
Now, if we call it's evolve method, it should see that the | ||
app1 evolve button was pressed and evolve app1 the thect | ||
generation. | ||
>>> status = view.evolve() | ||
>>> conn.sync() | ||
>>> conn.root()[generations_key]['foo.app1'] | ||
2 | ||
The demo evolver just writes the generation to a database key: | ||
>>> from zope.app.generations.demo import key | ||
>>> conn.root()[key] | ||
(2,) | ||
Which the returned status should indicate: | ||
>>> status['app'] | ||
u'foo.app1' | ||
>>> status['to'] | ||
2 | ||
Now, given that the database is at the maximum generation | ||
for app1, we can't evolve it further. Calling evolve again | ||
won't evolve anything: | ||
>>> status = view.evolve() | ||
>>> conn.sync() | ||
>>> conn.root()[generations_key]['foo.app1'] | ||
2 | ||
>>> conn.root()[key] | ||
(2,) | ||
as the status will indicate by returning a 'to' generation | ||
of 0: | ||
>>> status['app'] | ||
u'foo.app1' | ||
>>> status['to'] | ||
0 | ||
If the request doesn't have the key: | ||
>>> request.form.clear() | ||
Then calling evolve does nothing: | ||
>>> view.evolve() | ||
>>> conn.sync() | ||
>>> conn.root()[generations_key]['foo.app1'] | ||
2 | ||
>>> conn.root()[key] | ||
(2,) | ||
We'd better clean upp: | ||
>>> db.close() | ||
>>> tearDown() | ||
""" | ||
|
||
self.managers = managers = dict( | ||
zapi.getUtilitiesFor(ISchemaManager)) | ||
db = self._getdb() | ||
conn = db.open() | ||
try: | ||
generations = conn.root().get(generations_key, ()) | ||
request = self.request | ||
for key in generations: | ||
generation = generations[key] | ||
rkey = request_key_format % key | ||
if rkey in request: | ||
manager = managers[key] | ||
if generation >= manager.generation: | ||
return {'app': key, 'to': 0} | ||
context = Context() | ||
context.connection = conn | ||
generation += 1 | ||
manager.evolve(context, generation) | ||
generations[key] = generation | ||
get_transaction().commit() | ||
return {'app': key, 'to': generation} | ||
|
||
return None | ||
finally: | ||
get_transaction().abort() | ||
conn.close() | ||
|
||
def applications(self): | ||
"""Get information about database-generation status | ||
This method needs to use the component architecture, so | ||
we'll set it up: | ||
>>> from zope.app.tests.placelesssetup import setUp, tearDown | ||
>>> setUp() | ||
We also need a test request: | ||
>>> from zope.publisher.browser import TestRequest | ||
>>> request = TestRequest() | ||
We also need to give it a publication with a database: | ||
>>> class Publication: | ||
... pass | ||
>>> request.setPublication(Publication()) | ||
>>> from ZODB.tests.util import DB | ||
>>> db = DB() | ||
>>> request.publication.db = db | ||
We need to define some schema managers. We'll define two | ||
using the demo package: | ||
>>> from zope.app.generations.generations import SchemaManager | ||
>>> from zope.app.tests import ztapi | ||
>>> app1 = SchemaManager(0, 1, 'zope.app.generations.demo') | ||
>>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1') | ||
>>> app2 = SchemaManager(0, 0, 'zope.app.generations.demo') | ||
>>> ztapi.provideUtility(ISchemaManager, app2, 'foo.app2') | ||
And we need to record some data for them in the database. | ||
>>> from zope.app.generations.generations import evolve | ||
>>> evolve(db) | ||
This sets up the data and actually evolves app1: | ||
>>> conn = db.open() | ||
>>> conn.root()[generations_key]['foo.app1'] | ||
1 | ||
>>> conn.root()[generations_key]['foo.app2'] | ||
0 | ||
Now, let's increment app1's generation: | ||
>>> app1.generation += 1 | ||
so we can evolve it. | ||
Now we can create our view: | ||
>>> view = Managers(None, request) | ||
We call it's applications method to get data about | ||
application generations. We are required to call evolve | ||
first: | ||
>>> view.evolve() | ||
>>> data = list(view.applications()) | ||
>>> data.sort(lambda d1, d2: cmp(d1['id'], d2['id'])) | ||
>>> for info in data: | ||
... print info['id'] | ||
... print info['min'], info['max'], info['generation'] | ||
... print 'evolve?', info['evolve'] | ||
foo.app1 | ||
0 2 1 | ||
evolve? evolve-app-foo.app1 | ||
foo.app2 | ||
0 0 0 | ||
evolve? | ||
We'd better clean upp: | ||
>>> db.close() | ||
>>> tearDown() | ||
""" | ||
result = [] | ||
|
||
db = self._getdb() | ||
conn = db.open() | ||
try: | ||
managers = self.managers | ||
generations = conn.root().get(generations_key, ()) | ||
for key in generations: | ||
generation = generations[key] | ||
manager = managers.get(key) | ||
if manager is None: | ||
continue | ||
|
||
result.append({ | ||
'id': key, | ||
'min': manager.minimum_generation, | ||
'max': manager.generation, | ||
'generation': generation, | ||
'evolve': (generation < manager.generation | ||
and request_key_format % key | ||
or '' | ||
), | ||
}) | ||
|
||
return result | ||
finally: | ||
conn.close() |